<?php

namespace App\Libraries\Repositories;

use App\Libraries\Common\RedisHelper;
use App\Libraries\Contracts\IBaseRepository;
use App\Libraries\Criteria\parentCriteria;
use App\Libraries\Entities\BaseEntity;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Str;
use Prettus\Repository\Eloquent\BaseRepository as Repository;

class BaseRepository extends Repository implements IBaseRepository
{
    use RedisHelper;
    use DispatchesJobs;
    protected $PREFIX = 'Null';
    protected $hasSortCriteria = false;
    protected $sortType = 'asc';

    protected $class;
    protected $pField;

    public function model()
    {
        $this->class = Str::substr(class_basename(static::class), 0, -10);
        $config = config('repository.generator');

        return $config['rootNamespace'].$config['paths']['models'].'\\'.$this->PREFIX.'\\'.$this->class;
    }

    public function boot()
    {
        $this->class = Str::substr(class_basename(static::class), 0, -10);
    }

    /**
     * 统计数量.
     *
     * @param array $columns
     *
     * @return mixed
     *
     * @throws \Prettus\Repository\Exceptions\RepositoryException
     */
    public function counts(array $columns = ['*'])
    {
        $this->applyCriteria();
        $this->applyScope();
        $results = $this->model->count($columns);
        $this->resetModel();
        $this->resetScope();

        return $results;
    }

    /**
     * reset时需要清除scopeQuery和criteria，使repository可以使用单例模式.
     *
     * @throws \Prettus\Repository\Exceptions\RepositoryException
     */
    public function resetModel()
    {
        $this->scopeQuery = null;
        $this->resetCriteria();
        parent::resetModel();
    }

    /**
     * 获取某一列的最大值
     *
     * @param $column
     *
     * @return mixed
     *
     * @throws \Prettus\Repository\Exceptions\RepositoryException
     */
    public function max($column)
    {
        $this->applyCriteria();
        $this->applyScope();
        $results = $this->model->max($column);
        $this->resetModel();
        $this->resetScope();

        return $results;
    }

    /**
     * @param $field
     * @param null $pid
     * @param $level
     * @param $lazy
     * @param $columns
     *
     * @return array|mixed
     *
     * @throws \Prettus\Repository\Exceptions\RepositoryException
     */
    public function children($field, $pid, $level, $lazy, $columns)
    {
        $this->pField = $field;
        $tree = [];
        $this->level = $level;
        $this->pushCriteria(new parentCriteria($field, $pid));

        $children = $this->all();
        if (is_null($level)) {
            /** @var BaseEntity $value */
            foreach ($children as $value) {
                if ($childes = $this->children($this->pField, $value->GetId(), $level, $lazy, $columns)) {
                    $value['children'] = $childes;
                }
                $tree[] = $value;
            }
        } else {
            if ($this->level > 0) {
                /** @var BaseEntity $value */
                foreach ($children as $value) {
                    if ($childes = $this->children($this->pField, $value->GetId(), --$level, $lazy, $columns)) {
                        $value['children'] = $childes;
                    }
                    if ($lazy) {
                        $value['hasChild'] = !($this->model->where($this->pField, $value->GetId())->count() > 0);
                    }
                    $tree[] = $value;
                    $this->level = ++$level;
                }
            }
        }

        return $tree;
    }

    /**
     * 求某个字段之和.
     *
     * @param string $column
     *
     * @return mixed
     *
     * @throws \Prettus\Repository\Exceptions\RepositoryException
     */
    public function sum($column = 'id')
    {
        $this->applyCriteria();
        $this->applyScope();
        $results = $this->model->sum($column);
        $this->resetModel();

        return $results;
    }

    /**
     * Increment a column's value by a given amount.
     *
     * @param string $column
     * @param int    $id
     * @param int    $amount
     *
     * @return mixed
     */
    public function increment($column, $id, $amount = 1)
    {
        return $this->model->where($this->model->primaryKey, $id)->increment($column, $amount);
    }

    /**
     * Decrement a column's value by a given amount.
     *
     * @param string $column
     * @param int    $id
     * @param int    $amount
     *
     * @return mixed
     */
    public function decrement($column, $id, $amount = 1)
    {
        return $this->model->where($this->model->primaryKey, $id)->decrement($column, $amount);
    }

    /**
     * Find data by id.
     *
     * @param       $id
     * @param array $columns
     *
     * @return mixed
     */
    public function find($id, $columns = ['*'])
    {
        try {
            $this->applyCriteria();
            $this->applyScope();
            $model = $this->model->findOrFail($id, $columns);
            $this->resetModel();

            return $this->parserResult($model);
        } catch (\Exception $e) {
            return null;
        }
    }

    /**
     * 保存数据.
     *
     * @param BaseEntity $data
     * @param null       $id
     *
     * @return mixed
     *
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function SaveData(BaseEntity $data, $id = null)
    {
        $result = empty($id) ? $this->createData($data) : $this->updateData($data, $id);
        $this->resetRedisData();

        return $result;
    }

    /**
     * @param BaseEntity $data
     * @param $id
     *
     * @return mixed
     *
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    protected function updateData(BaseEntity $data, $id)
    {
        if ($data->useUpdated()) {
            $data->setUpdated();
        }

        return $this->update($data->toArray(), $id);
    }

    /**
     * @param BaseEntity $data
     *
     * @return mixed
     *
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    protected function createData(BaseEntity $data)
    {
        if ($data->useCreated()) {
            $data->setCreated();
        }

        return $this->create($data->toArray());
    }

    /**
     * 批量更新.
     *
     * @param $field
     * @param $values
     * @param $data
     *
     * @return mixed
     */
    public function UpdateAll($field, $values, $data)
    {
        return $this->model->whereIn($field, $values)->update($data);
    }
}
